package pt.inescid.components.policyengine.heimdall.pap.base;

import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.base.EventObj;
import pt.inescid.components.policyengine.heimdall.base.EventRelevance;
import pt.inescid.components.policyengine.heimdall.base.User;
import pt.inescid.components.policyengine.heimdall.commons.TimeUtilities;
import pt.inescid.components.policyengine.heimdall.commons.Utilities;
import pt.inescid.components.policyengine.heimdall.database.EventObjectDAO;
import pt.inescid.components.policyengine.heimdall.pdp.base.ModelInstance;
import pt.inescid.components.policyengine.heimdall.pdp.base.PurgeInstance;
import pt.inescid.components.policyengine.heimdall.pdp.base.RuleInstance;

public class CesDefinition {

	public static final String ROOT_CES = "root";
	public static final String ALL_EVENT_CES = "AllEvents";
	public static final String PAST_EVENT_CES = "PastEvents";
	public static final String FUTURE_EVENT_CES = "FutureEvents";
	
	//  ----------------------------------------------------------
//  ------------------------   STATIC variables   ------------------------
//  ----------------------------------------------------------
	protected static int cesCounter = 0;
	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      instance variables       =============================
	=============                    =============================
	==============================================================
	=========================================================== */

	private int id;
	private String name;
	private CesDefinition baseCes;
	private RuleDefinition restriction;
	private RuleDefinition globalRestriction;
	private RuleDefinition globalNeutralRestriction;
	private Vector cesParameters;
	
	private RuleInstance auxRestrictionInstance;

	private long withinMilli;
	private RuleDefinition temporalRestriction;
	
	private PurgeInstance purge;
	private RuleDefinition privacyFilter;
	private RuleInstance auxPrivacyFilterInstance;


	private ModelDefinition modelDef;
	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      CONSTRUCTORS       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	
	public CesDefinition(
			String name, 
			String baseCes, 
			ModelDefinition modelDef, 
			String withinMilli,
			ModelDefinition currentModelDef) {
		super();
		id = cesCounter++;
		if (baseCes == null) {
			baseCes = "PastEvents";
		}
		
		this.name = name;
		this.baseCes = currentModelDef.getCes(baseCes);
		this.modelDef = modelDef;
				
		this.withinMilli = TimeUtilities.parseTimeQty(withinMilli);
		
		if (this.withinMilli == 0) {
			this.temporalRestriction = null;
		}
		else {
			// Future Events
			CompositeDefinition currentCondition = null;
			CompositeDefinition nextCondition = null;
			EventFieldDefinition ceOperand = null;
			EventFieldDefinition otherEventOperand = null;

			currentCondition = new CompositeDefinition(
					RuleDefinition.LESSER_THAN_OPERATOR,
					null);
			
			
			nextCondition = new CompositeDefinition(
					RuleDefinition.SUBTRACT_OPERATOR,
					null);
			
			ceOperand = new EventFieldDefinition(
					modelDef,
					RuleDefinition.EVENT_FIELD_EXPRESSION,
					RuleDefinition.EXPRESSION_VALUE_CODES[
                    RuleDefinition.TIMESTAMP_VALUE],
                    nextCondition,
			        "ce.executionTime");
			
			otherEventOperand = new EventFieldDefinition(
					modelDef,
					RuleDefinition.EVENT_FIELD_EXPRESSION,
					RuleDefinition.EXPRESSION_VALUE_CODES[
                    RuleDefinition.TIMESTAMP_VALUE],
                    nextCondition,
                    this.name + ".executionTime");
			
			
			if (this.isObligation()) {
				
				// (x.time-ce.time) < within
				nextCondition.addOperand(otherEventOperand);
				nextCondition.addOperand(ceOperand);
			}
			else {
				
				// (ce.time-x.time) < within
				nextCondition.addOperand(ceOperand);
				nextCondition.addOperand(otherEventOperand);
			}
			
			currentCondition.addOperand(nextCondition);
			
			currentCondition.addOperand(new SimpleDefinition(
					RuleDefinition.LITERAL_EXPRESSION,
					RuleDefinition.EXPRESSION_VALUE_CODES[
                    RuleDefinition.TIMESTAMP_VALUE],
                    currentCondition,
                    String.valueOf(this.withinMilli)));
			
			this.temporalRestriction = currentCondition;
		}

	}

	
	
	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      methods       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	
	
	
	
	public RuleDefinition getRestriction() {
		return restriction;
	}

	public void setRestriction(RuleDefinition newExpression, 
			RuleDefinition previousLevelExp) {
		
		if (previousLevelExp == null) {
			restriction = newExpression;
		}
		else {
			((CompositeDefinition) previousLevelExp).addOperand(newExpression);
		}
	}


	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

//  ----------------------------------------------------------
//  ------------------------   toString   ------------------------
//  ----------------------------------------------------------
	public String toString() {
		String result = name + " = " + (baseCes == null ? ROOT_CES : 
			baseCes.getName()) + "@[" + 
			(restriction == null ? "" : restriction.getFunction()) + "]"; 
		
		return (result);
	}

//  ----------------------------------------------------------
//  ------------------------   getFunction   ------------------------
//  ----------------------------------------------------------
	public String getFunction() {
		String result = name + " = " + (baseCes == null ? ROOT_CES : 
			baseCes.getName()) + "@[" + 
			(restriction == null ? "" : restriction.getFunction()) + "]"; 
		
		return (result);
	}

	public CesDefinition getBaseCes() {
		return baseCes;
	}

	public void setBaseCes(CesDefinition baseCes) {
		this.baseCes = baseCes;
	}
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}


	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      shouldPurge       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public boolean shouldPurge(EventObj ce) {
		return (purge.shouldPurge(ce));
	} // shouldPurge
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      purge       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public void purge(User currentUser, 
			ModelDefinition currentModelDef) {
		
		EventObjectDAO.purge(currentUser, currentModelDef, this);
	} // purge
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      isRelevant       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public boolean isRelevant(EventObj ce) {
		ModelInstance currentModel = new ModelInstance(ce);
		RuleDefinition currentRestriction = restriction;
		CompositeDefinition restrictionsAnd = null;
		CesDefinition currentCes = baseCes;
		
		// 'and' the current ces restriction with all base set 
		// restrictions
		if (currentCes != null) {
			restrictionsAnd = new CompositeDefinition(
					RuleDefinition.LOGICAL_AND_OPERATOR,
					null);
			restrictionsAnd.addOperand(restriction);
			
			while (!currentCes.getName().equals(ROOT_CES)) {
				restrictionsAnd.addOperand(currentCes.getRestriction());
				currentCes = currentCes.getBaseCes();
			}
			
			if (restrictionsAnd.getOperands().size() != 0) {
				currentRestriction = restrictionsAnd;
			}
		}
		
		if (auxRestrictionInstance == null) {
			if (restriction == null) {
				return (true);
			}
			else {
				currentRestriction = currentRestriction.getTimeIndependentCopy();
				
				if (currentRestriction == null) {
					return (true);
				}
				else {
					currentRestriction = currentRestriction.getCeIndependentCopy();
					
					if (currentRestriction == null) {
						return (true);
					}
					else {
						auxRestrictionInstance = RuleInstance.getInstance(
								currentRestriction, currentModel, null);
					}
				}
			}
		}

		if (auxRestrictionInstance != null) {
			return (((Boolean) auxRestrictionInstance.evaluate(currentModel,
					false)).
					booleanValue());
		}
		else {
			return (true);
		}
	} // isRelevant

	
	
	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getCesParameters       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector getCesParameters() {
		if (cesParameters != null) {
			return (cesParameters); 
		}
		
		cesParameters = new Vector();
		if (baseCes != null) {
			Utilities.joinVectors(cesParameters, baseCes.getCesParameters());
		}

		Vector relevantFields = null;
		if (restriction != null) {
			relevantFields = restriction.getRelevantEventFields();
			
			if (relevantFields == null) {
				relevantFields = new Vector();
			}

			// the execution time is always necessary to evaluate if
			// an event is past or future
			relevantFields.add(new EventRelevance(
				modelDef,
				this.name,
				null,
				".executionTime",
				RuleDefinition.TIMESTAMP_VALUE,
				false));		
			
			// set the ces name in all the fields
			for (int i = 0; i < relevantFields.size(); i++) {
				((EventRelevance) relevantFields.elementAt(i)).setCesName(name);
			}
			
			Utilities.joinParametersNoDuplicates(cesParameters,
					relevantFields);
		}
		
		
		return (cesParameters);
	} // getCesParameters

	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getRelevantEventFields       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector getRelevantEventFields() {
		Vector currentRelevant = null;
		
		if (privacyFilter != null) {
			currentRelevant = privacyFilter.getRelevantEventFields();
		}
		
		return (currentRelevant);
	} // getRelevantEventFields


	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getGlobalRestriction       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public RuleDefinition getGlobalRestriction() {
		if (globalRestriction != null) {
			return (globalRestriction); 
		}
		
		RuleDefinition baseRestriction = null;
		if (baseCes != null) {
			baseRestriction = baseCes.getGlobalRestriction();
		}

		if (baseRestriction == null) {
			globalRestriction = restriction;
		}
		else {
			globalRestriction = new CompositeDefinition(
					RuleDefinition.LOGICAL_AND_OPERATOR,
					null);
			((CompositeDefinition) globalRestriction).
					addOperand(baseRestriction);
			((CompositeDefinition) globalRestriction).
					addOperand(restriction);
		}

		return (globalRestriction);
	} // getGlobalRestriction

	
	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      equals       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public boolean equals(Object a) {
		return (this.getName().equals(
				((CesDefinition) a).getName()));
	}

	
	
		
		
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getGlobalNeutralRestriction       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public RuleDefinition getGlobalNeutralRestriction() {
		if (globalNeutralRestriction != null) {
			return (globalNeutralRestriction); 
		}
		
		globalNeutralRestriction = 
			globalRestriction.getTimeIndependentCopy();
		
		return (globalNeutralRestriction);
	} // getGlobalNeutralRestriction

	
	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      isObligation       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public boolean isObligation() {
		CesDefinition currentCes = this;
		while (!currentCes.getName().equals(CesDefinition.ROOT_CES)) {
			if (currentCes.getName().equals(CesDefinition.FUTURE_EVENT_CES)) {
			
				return (true);
			}
			
			currentCes = currentCes.getBaseCes();
		}
		
		return (false);
	} // isObligation

	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      filterEventsForPrivacy       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector filterEventsForPrivacy(Vector allRelevantEvents,
			ModelDefinition currentModelDef) {
		Vector filteredEvents = new Vector();
		
		if (privacyFilter == null) {
			return (allRelevantEvents);
		}
		else {
			EventObj currentEvent = null;
			ModelInstance currentModel = null;
			for (int i = 0; i < allRelevantEvents.size(); i++) {
				currentEvent = (EventObj) allRelevantEvents.elementAt(i);
				currentModel = new ModelInstance(currentEvent);

				auxPrivacyFilterInstance = RuleInstance.getInstance(
						privacyFilter, currentModel, null);

				if (auxPrivacyFilterInstance == null) {
					return (allRelevantEvents);
				}
				else if (((Boolean) auxPrivacyFilterInstance.evaluate(
						currentModel,
						false)).
						booleanValue()) {
					
					filteredEvents.add(currentEvent);
				}
			}
		}		

		return (filteredEvents);
	} // filterEventsForPrivacy


	
	
	
	public RuleDefinition getPrivacyFilter() {
		return privacyFilter;
	}






	public void setPrivacyFilter(RuleDefinition newExpression, 
			RuleDefinition previousLevelExp) {
		
		if (previousLevelExp == null) {
			this.privacyFilter = newExpression;
		}
		else {
			((CompositeDefinition) previousLevelExp).addOperand(
					newExpression);
		}
	}



	public long getWithinMilli() {
		return withinMilli;
	}


	public RuleDefinition getTemporalRestriction() {
		return temporalRestriction;
	}


	public void setRestriction(RuleDefinition restriction) {
		this.restriction = restriction;
	}
	
	public PurgeInstance getPurge() {
		return purge;
	}


	public void setPurge(PurgeInstance purge) {
		this.purge = purge;
	}






	public ModelDefinition getModelDef() {
		return modelDef;
	}



	
}; // DomainDefinition
